package society.framework.core.xml;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Stack;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import society.framework.core.Automaton;
import society.framework.core.communication.Message;

/**
 * Extension of the BaseXMLAutomaton which implements the deserialization operations.
 * It uses a SAX parser for deserialization.
 * @author Micu Andrei
 *
 */
public abstract class DeserializableOnlyXMLAutomaton extends BaseXMLAutomaton {
	protected static final String FINISHED_EXCEPTION_MESSAGE = "FINISHED";
	
	protected static final String AUTOMATON_TAG = "automaton";
	protected static final String CURRENT_MESSAGE_TAG = "current_message";
	protected static final String MESSAGE_LIST_TAG = "message_list";
	protected static final String MESSAGE_TAG = "message";
	protected static final String VARIABLES_TAG = "variables";
	protected static final String VARIABLE_TAG = "variable";
	protected static final String STATES_TAG = "states";
	protected static final String STATE_TAG = "state";
	protected static final String CODE_TAG = "code";
	protected static final String TRANSITION_TAG = "transition";
	protected static final String TRANSITION_GROUP_TAG = "transition_group";
	protected static final String WHILE_TAG = "while";
	protected static final String DO_WHILE_TAG = "do_while";
	protected static final String FOR_TAG = "for";
	protected static final String IF_TAG = "if";
	protected static final String ELSE_TAG = "else";
	protected static final String SWITCH_TAG = "switch";
	protected static final String CASE_TAG = "case";
	protected static final String DEFAULT_TAG = "default";
	protected static final String EXECUTE_TAG = "execute";
	protected static final String GET_NEXT_MESSAGE_TAG = "get_next_message";
	protected static final String SEND_MESSAGE_TAG = "send_message";
	protected static final String BREAK_TAG = "break";
	protected static final String CONTINUE_TAG = "continue";
	protected static final String RETURN_TAG = "return";

	protected static final String NAME_ATTRIBUTE = "name";
	protected static final String REGISTER_AT_START_ATTRIBUTE = "register_at_start";
	protected static final String VALUE_ATTRIBUTE = "value";
	protected static final String TEST_ATTRIBUTE = "test";
	protected static final String ASSIGN_ATTRIBUTE = "assign";
	protected static final String STEP_ATTRIBUTE = "step";
	protected static final String FROM_ATTRIBUTE = "from";
	protected static final String TO_ATTRIBUTE = "to";
	protected static final String EXPRESSION_ATTRIBUTE = "expression";
	protected static final String TIMER_ATTRIBUTE = "timer";
	protected static final String DATA_ATTRIBUTE = "data";
	protected static final String CURRENT_STATE_ATTRIBUTE = "current_state";
	
	protected static final String GET_CURRENT_MESSAGE_FROM_FUNCTION = "getCurrentMessageFrom";
	protected static final String GET_CURRENT_MESSAGE_DATA_FUNCTION = "getCurrentMessageData";
	protected static final String IS_CURRENT_MESSAGE_NULL_FUNCTION = "isCurrentMessageNull";
	protected static final String EXECUTE_TRANSITION_FUNCTION = "executeTransition";
	protected static final String SIZE_FUNCTION = "size";
	protected static final String SPLIT_FUNCTION = "split";
	protected static final String PARSE_INTEGER_FUNCTION = "parseInteger";


	/* **************************************** DESERIALIZATION  **************************************** */
	
	@Override
	public boolean deserializeFromString(String serialization) {
		return deserializeFromStream(new ByteArrayInputStream(serialization.getBytes()));
	}
	
	protected DeserializationAutomaton deserializationAutomaton;

	@Override
	public boolean deserializeFromStream(InputStream serialization) {
		try{
			deserializationAutomaton = new DeserializationAutomaton();
			deserializationAutomaton.start();
			
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			xr.setContentHandler(new AutomatonSAXEventHandler());
			InputSource inputSource = new InputSource(new BufferedInputStream(serialization){

				@Override
				public void close() throws IOException {
					//this wrapping stream prevents the original stream from closing by the SAX Parser
				}
				
			});
			xr.parse(inputSource);
			//should jump to exception if parsing is finished
			deserializationAutomaton = null;
			return false;

		} catch (Exception e) {
			if (e.getMessage().equals(FINISHED_EXCEPTION_MESSAGE)) {
				deserializationAutomaton.waitForFinish();
				deserializationAutomaton = null;
				return true;
			} else {
				deserializationAutomaton = null;
				return false;
			}
		}
	}
	
	protected enum EventType { StartElement, EndElement }
	
	protected class Event{
		public EventType eventType;
		public String tagName;
		public HashMap<String, String> attributes;
		
		public Event(){}
		
		public Event(EventType eventType, String tagName, HashMap<String, String> attributes) {
			this.eventType = eventType;
			this.tagName = tagName;
			this.attributes = attributes;
		}
		
	}
	
	protected class AutomatonSAXEventHandler extends DefaultHandler{

		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
			HashMap<String, String> attributesMap = new HashMap<String, String>();
			for(int i=0; i<attributes.getLength(); i++){
				attributesMap.put(attributes.getQName(i), attributes.getValue(i));
			}
			deserializationAutomaton.addMessage(new Message(null, new Event(EventType.StartElement, localName, attributesMap)));
		}

		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException {
			deserializationAutomaton.addMessage(new Message(null, new Event(EventType.EndElement, localName, null)));
			if(localName.equals(AUTOMATON_TAG)){
				throw new SAXException(FINISHED_EXCEPTION_MESSAGE);
			}
		}
		
	}
	
	/**
	 * Automaton that takes the messages from the SAX Parser and creates the states, the transitions
	 * and the variables of the XMLAutomaton.
	 * @author Micu Andrei
	 *
	 */
	protected class DeserializationAutomaton extends Automaton{
		protected static final String ENTRY_STATE = "ENTRY_STATE";
		protected static final String MERGE_IF_ELSE_STATE = "MERGE_IF_ELSE_STATE";
		protected static final String MERGE_SWITCH_CASE_STATE = "MERGE_SWITCH_CASE_STATE";
		protected static final String MERGE_SWITCH_DEFAULT_STATE = "MERGE_SWITCH_DEFAULT_STATE";
		protected static final String ATOMIC_INSTRUCTION_STATE = "ATOMIC_INSTRUCTION_STATE";
		protected static final String END_STATE = "END_STATE";
		protected static final String ERROR_STATE = "ERROR_STATE";
		
		protected Stack<CodeContainer> containerStack;	//stack used to keep track of the code containers when adding instructions
		protected Event currentEvent = null;			//reference to the event in the last message took from the message list
		protected String errorMessage = null;

		public DeserializationAutomaton() {
			super();
			containerStack = new Stack<CodeContainer>();
			currentStateName = ENTRY_STATE;
			

			/**** ENTRY STATE ****/
			states.put(ENTRY_STATE, new NullState());
			
			states.get(ENTRY_STATE).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(AUTOMATON_TAG)){			//begin the automaton construction
							return AUTOMATON_TAG;
						}
					}
					errorMessage = ENTRY_STATE;
					return ERROR_STATE;
				}
				
			};
			
			
			/* AUTOMATON */
			states.put(AUTOMATON_TAG, new State(){

				@Override
				public void stateCode() {
					if(currentEvent.eventType == EventType.StartElement && currentEvent.tagName.equals(AUTOMATON_TAG)){
						DeserializableOnlyXMLAutomaton.this.name = currentEvent.attributes.get(NAME_ATTRIBUTE);
						DeserializableOnlyXMLAutomaton.this.currentStateName = currentEvent.attributes.get(CURRENT_STATE_ATTRIBUTE);
						if(currentEvent.attributes.containsKey(REGISTER_AT_START_ATTRIBUTE) &&
								currentEvent.attributes.get(REGISTER_AT_START_ATTRIBUTE).equals(".T.")){
							DeserializableOnlyXMLAutomaton.this.setRegisterAtStart(true);
						}
					}
				}
				
			});
			
			states.get(AUTOMATON_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(CURRENT_MESSAGE_TAG)){			//begin declaring the current message
							return CURRENT_MESSAGE_TAG;
							
						} else if(currentEvent.tagName.equals(MESSAGE_LIST_TAG)){		//begin declaring the message list
							return MESSAGE_LIST_TAG;
							
						} else if(currentEvent.tagName.equals(VARIABLES_TAG)){			//begin declaring variables
							return VARIABLES_TAG;
							
						} else if(currentEvent.tagName.equals(STATES_TAG)){				//begin declaring states
							return STATES_TAG;
						}
					} else {
						if(currentEvent.tagName.equals(AUTOMATON_TAG)){					//end automaton construction
							return END_STATE;
						}
					}
					errorMessage = AUTOMATON_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* CURRENT_MESSAGE */
			states.put(CURRENT_MESSAGE_TAG, new State(){

				@Override
				public void stateCode() {
					//create the message and assign it to the currentMessage
					currentMessage = new Message(currentEvent.attributes.get(FROM_ATTRIBUTE),
												currentEvent.attributes.get(DATA_ATTRIBUTE));
				}
				
			});
			
			states.get(CURRENT_MESSAGE_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.EndElement){
						if(currentEvent.tagName.equals(CURRENT_MESSAGE_TAG)){			//end declaring the current message
							return AUTOMATON_TAG;
						}
					}
					errorMessage = CURRENT_MESSAGE_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* MESSAGE_LIST */
			states.put(MESSAGE_LIST_TAG, new NullState());
			
			states.get(MESSAGE_LIST_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(MESSAGE_TAG)){				//begin declaring a message
							return MESSAGE_TAG;
						}
						
					} else {
						if(currentEvent.tagName.equals(MESSAGE_LIST_TAG)){			//end declaring the message list
							return AUTOMATON_TAG;
						}
					}
					errorMessage = MESSAGE_LIST_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* MESSAGE */
			states.put(MESSAGE_TAG, new State(){

				@Override
				public void stateCode() {
					//create the message and add it to the list
					DeserializableOnlyXMLAutomaton.this.messageList.add(new 
							Message(currentEvent.attributes.get(FROM_ATTRIBUTE),
									currentEvent.attributes.get(DATA_ATTRIBUTE)));
				}
				
			});
			
			states.get(MESSAGE_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.EndElement){
						if(currentEvent.tagName.equals(MESSAGE_TAG)){			//end declaring the message
							return MESSAGE_LIST_TAG;
						}
					}
					errorMessage = MESSAGE_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* VARIABLES */
			states.put(VARIABLES_TAG, new NullState());
			
			states.get(VARIABLES_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(VARIABLE_TAG)){				//begin declaring a variable
							return VARIABLE_TAG;
						}
						
					} else {
						if(currentEvent.tagName.equals(VARIABLES_TAG)){				//end declaring variables
							return AUTOMATON_TAG;
						}
					}
					errorMessage = VARIABLES_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* VARIABLE */
			states.put(VARIABLE_TAG, new State(){

				@Override
				public void stateCode() {
					//add the variable to the HashMap and set it's default value if it's specified
					Object defaultValue;
					AssignInstruction ai = new AssignInstruction();
					ai.leftOperand = constructExpression(currentEvent.attributes.get(NAME_ATTRIBUTE));
					if((defaultValue = currentEvent.attributes.get(VALUE_ATTRIBUTE))!=null){
						ai.rightOperand = constructExpression((String)defaultValue);
					} else {
						ValueInstruction vi = new ValueInstruction();
						vi.value = 0;
						ai.rightOperand = vi;
					}
					ai.code();
				}
				
			});
			
			states.get(VARIABLE_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.EndElement){
						if(currentEvent.tagName.equals(VARIABLE_TAG)){				//end declaring a variable
							return VARIABLES_TAG;
						}
					}
					errorMessage = VARIABLE_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* STATES */
			states.put(STATES_TAG, new NullState());
			
			states.get(STATES_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(STATE_TAG)){					//begin declaring a state
							return STATE_TAG;
						}
					} else {
						if(currentEvent.tagName.equals(STATES_TAG)){				//end declaring states
							return AUTOMATON_TAG;
						}
					}
					errorMessage = STATES_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* STATE */
			states.put(STATE_TAG, new State(){

				@Override
				public void stateCode() {
					if(currentEvent.eventType == EventType.StartElement && currentEvent.tagName.equals(STATE_TAG)){
						//add the state to the states list
						XMLState newXMLState = new XMLState();
						DeserializableOnlyXMLAutomaton.this.states.put(currentEvent.attributes.get(NAME_ATTRIBUTE), newXMLState);
						containerStack.push(newXMLState);
					}
				}
				
			});
			
			states.get(STATE_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(CODE_TAG)){						//begin declaring the code in the state
							return CODE_TAG;
						} else if(currentEvent.tagName.equals(TRANSITION_TAG)){			//begin declaring a transition in the state
							return TRANSITION_TAG;
						} else if(currentEvent.tagName.equals(TRANSITION_GROUP_TAG)){	//begin declaring a transition group in the state
							return TRANSITION_GROUP_TAG;
						}
					} else {
						if(currentEvent.tagName.equals(STATE_TAG)){						//end declaring a state
							containerStack.pop();
							return STATES_TAG;
						}
					}
					errorMessage = STATE_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* TRANSITION */
			states.put(TRANSITION_TAG, new State(){

				@Override
				public void stateCode() {
					XMLTransition newXMLTransition = new XMLTransition();
					if(containerStack.peek() instanceof State){								//case when it's inside a state
						((State) containerStack.peek()).transition = newXMLTransition;
					} else if(containerStack.peek() instanceof TransitionGroup){			//case when it's inside a transition group
						((TransitionGroup) containerStack.peek()).transitions
						.put(currentEvent.attributes.get(NAME_ATTRIBUTE), newXMLTransition);
					}
					containerStack.push(newXMLTransition);
				}
				
			});
			
			states.get(TRANSITION_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* TRANSITION GROUP */
			states.put(TRANSITION_GROUP_TAG, new State(){

				@Override
				public void stateCode() {
					if(currentEvent.eventType == EventType.StartElement && currentEvent.tagName.equals(TRANSITION_GROUP_TAG)){
						XMLTransitionGroup newXMLTransitionGroup = new XMLTransitionGroup();
						if(containerStack.peek() instanceof State){							//case when it's inside a state
							((State) containerStack.peek()).transition = newXMLTransitionGroup;
						} else if(containerStack.peek() instanceof TransitionGroup){		//case when it's inside a transition group
							((TransitionGroup) containerStack.peek()).transitions
							.put(currentEvent.attributes.get(NAME_ATTRIBUTE), newXMLTransitionGroup);
						}
						containerStack.push(newXMLTransitionGroup);
					}
				}
				
			});
			
			states.get(TRANSITION_GROUP_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(CODE_TAG)){				//begin declaring the code in the transition group
							return CODE_TAG;
						} else if(currentEvent.tagName.equals(TRANSITION_TAG)){	//begin declaring a transition in the transition group
							return TRANSITION_TAG;
						} else if(currentEvent.tagName.equals(TRANSITION_GROUP_TAG)){	//begin declaring a transition group in
							return TRANSITION_GROUP_TAG;								//the transition group
						}
						
					} else {
						if(currentEvent.tagName.equals(TRANSITION_GROUP_TAG)){			//end declaring a transition group
							containerStack.pop();
							if(containerStack.peek() instanceof State){
								return STATE_TAG;
							} else {
								return TRANSITION_GROUP_TAG;
							}
						}
					}
					errorMessage = TRANSITION_GROUP_TAG;
					return ERROR_STATE;
				}
				
			};

			
			/* CODE STATE */
			states.put(CODE_TAG, new NullState());
			
			states.get(CODE_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType == EventType.StartElement){
						if(currentEvent.tagName.equals(WHILE_TAG)){					//start declaring a while
							return WHILE_TAG;
						} else if(currentEvent.tagName.equals(DO_WHILE_TAG)){		//start declaring a do-while
							return DO_WHILE_TAG;
						} else if(currentEvent.tagName.equals(FOR_TAG)){			//start declaring a for
							return FOR_TAG;
						} else if(currentEvent.tagName.equals(IF_TAG)){				//start declaring an if
							return IF_TAG;
						} else if(currentEvent.tagName.equals(ELSE_TAG)){			//start declaring an else
							return ELSE_TAG;
						} else if(currentEvent.tagName.equals(SWITCH_TAG)){			//start declaring a switch
							return SWITCH_TAG;
						} else {						//if all the above fail, then the instruction may be an atomic instruction
							return ATOMIC_INSTRUCTION_STATE;
						}
						
					} else {
						if(currentEvent.tagName.equals(CODE_TAG)){	//end declaring the code inside a state or transition group
							if(containerStack.peek() instanceof State){
								return STATE_TAG;
							} else if(containerStack.peek() instanceof TransitionGroup){
								return TRANSITION_GROUP_TAG;
							}
						} else if(currentEvent.tagName.equals(TRANSITION_TAG)){		//end declaring a transition
							containerStack.pop();
							if(containerStack.peek() instanceof State){
								return STATE_TAG;
							} else if(containerStack.peek() instanceof TransitionGroup){
								return TRANSITION_GROUP_TAG;
							}
						} else if(currentEvent.tagName.equals(WHILE_TAG)){			//end declaring a while
							containerStack.pop();
							return CODE_TAG;
						} else if(currentEvent.tagName.equals(DO_WHILE_TAG)){		//end declaring a do-while
							containerStack.pop();
							return CODE_TAG;
						} else if(currentEvent.tagName.equals(FOR_TAG)){			//end declaring a for
							containerStack.pop();
							return CODE_TAG;
						} else if(currentEvent.tagName.equals(IF_TAG)){				//end declaring an if
							containerStack.pop();
							return CODE_TAG;
						} else if(currentEvent.tagName.equals(ELSE_TAG)){			//end declaring an else
							containerStack.pop();
							return MERGE_IF_ELSE_STATE; //merge the last if and else
						} else if(currentEvent.tagName.equals(CASE_TAG)){			//end declaring a case
							containerStack.pop();
							return MERGE_SWITCH_CASE_STATE; //merge the last switch and case
						} else if(currentEvent.tagName.equals(DEFAULT_TAG)){		//end declaring a default
							containerStack.pop();
							return MERGE_SWITCH_DEFAULT_STATE; //merge the last switch and default
						}
					}
					errorMessage = CODE_TAG;
					return ERROR_STATE;
				}
				
			};
			
			
			/* WHILE */
			states.put(WHILE_TAG, new State(){

				@Override
				public void stateCode() {
					//add the while to the parent code container
					WhileInstruction newWhile = new WhileInstruction();
					newWhile.testInstruction = constructExpression(currentEvent.attributes.get(TEST_ATTRIBUTE));
					containerStack.peek().addInstruction(newWhile);
					containerStack.push(newWhile);
				}
				
			});
			
			states.get(WHILE_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* DO WHILE */
			states.put(DO_WHILE_TAG, new State(){

				@Override
				public void stateCode() {
					//add the do-while to the parent code container
					DoWhileInstruction newDoWhile = new DoWhileInstruction();
					newDoWhile.testInstruction = constructExpression(currentEvent.attributes.get(TEST_ATTRIBUTE));
					containerStack.peek().addInstruction(newDoWhile);
					containerStack.push(newDoWhile);
				}
				
			});
			
			states.get(DO_WHILE_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* FOR */
			states.put(FOR_TAG, new State(){

				@Override
				public void stateCode() {
					//add the for to the parent code container
					ForInstruction newFor = new ForInstruction();
					newFor.assignInstruction = constructExpression(currentEvent.attributes.get(ASSIGN_ATTRIBUTE));
					newFor.testInstruction = constructExpression(currentEvent.attributes.get(TEST_ATTRIBUTE));
					newFor.stepInstruction = constructExpression(currentEvent.attributes.get(STEP_ATTRIBUTE));
					containerStack.peek().addInstruction(newFor);
					containerStack.push(newFor);
				}
				
			});
			
			states.get(FOR_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* IF */
			states.put(IF_TAG, new State(){

				@Override
				public void stateCode() {
					//add the if to the parent code container
					IfElseInstruction newIf = new IfElseInstruction();
					newIf.testInstruction = constructExpression(currentEvent.attributes.get(TEST_ATTRIBUTE));
					containerStack.peek().addInstruction(newIf);
					containerStack.push(newIf);
				}
				
			});
			
			states.get(IF_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* ELSE */
			states.put(ELSE_TAG, new State(){

				@Override
				public void stateCode() {
					//add the else to the parent code container
					IfElseInstruction newElse = new IfElseInstruction();
					containerStack.peek().addInstruction(newElse);
					containerStack.push(newElse);
				}
				
			});
			
			states.get(ELSE_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* MERGE IF-ELSE */
			states.put(MERGE_IF_ELSE_STATE, new State(){

				@Override
				public void stateCode() {
					//merge the last if and else in the current instruction block
					IfElseInstruction elseInstruction = (IfElseInstruction) containerStack.peek().removeInstruction();
					IfElseInstruction ifInstruction = (IfElseInstruction) containerStack.peek().removeInstruction();
					ifInstruction.failInstruction = elseInstruction.succeedInstruction;
					containerStack.peek().addInstruction(ifInstruction);
				}
				
			});
			
			states.get(MERGE_IF_ELSE_STATE).transition = new SimpleTransition(CODE_TAG);
			
			
			/* SWITCH */
			states.put(SWITCH_TAG, new State(){

				@Override
				public void stateCode() {
					if(currentEvent.eventType == EventType.StartElement && currentEvent.tagName.equals(SWITCH_TAG)){
						//add a switch instruction in the current instruction block
						SwitchInstruction newSwitchInstruction = new SwitchInstruction();
						newSwitchInstruction.argumentInstruction = constructExpression(currentEvent.attributes.get(VALUE_ATTRIBUTE));
						containerStack.peek().addInstruction(newSwitchInstruction);
					}
				}
				
			});
			
			states.get(SWITCH_TAG).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType.equals(EventType.StartElement)){
						if(currentEvent.tagName.equals(CASE_TAG)){					//begin declaring a case
							return CASE_TAG;
						} else if(currentEvent.tagName.equals(DEFAULT_TAG)){		//begin declaring a default
							return DEFAULT_TAG;
						}
					} else {
						if(currentEvent.tagName.equals(SWITCH_TAG)){				//end declaring a switch
							return CODE_TAG;
						}
					}
					return null;
				}
				
			};
			
			
			/* CASE */
			states.put(CASE_TAG, new State(){

				@Override
				public void stateCode() {
					//add a case instruction in the current instruction block
					CaseInstruction newCaseInstruction = new CaseInstruction();
					newCaseInstruction.label = constructExpression(currentEvent.attributes.get(VALUE_ATTRIBUTE));
					containerStack.peek().addInstruction(newCaseInstruction);
					containerStack.push(newCaseInstruction);
				}
				
			});
			
			states.get(CASE_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* DEFAULT */
			states.put(DEFAULT_TAG, new State(){

				@Override
				public void stateCode() {
					//add a default instruction in the current instruction block
					CaseInstruction newDefaultInstruction = new CaseInstruction();
					containerStack.peek().addInstruction(newDefaultInstruction);
					containerStack.push(newDefaultInstruction);
				}
				
			});
			
			states.get(DEFAULT_TAG).transition = new SimpleTransition(CODE_TAG);
			
			
			/* MERGE SWITCH-CASE */
			states.put(MERGE_SWITCH_CASE_STATE, new State(){

				@Override
				public void stateCode() {
					//merge the last switch and case in the current instruction block
					CaseInstruction caseInstruction = (CaseInstruction) containerStack.peek().removeInstruction();
					SwitchInstruction switchInstruction = (SwitchInstruction) containerStack.peek().removeInstruction();
					switchInstruction.caseInstructionsList.add(caseInstruction);
					containerStack.peek().addInstruction(switchInstruction);
				}
				
			});
			
			states.get(MERGE_SWITCH_CASE_STATE).transition = new SimpleTransition(SWITCH_TAG);
			
			
			/* MERGE SWITCH_DEFAULT */
			states.put(MERGE_SWITCH_DEFAULT_STATE, new State(){

				@Override
				public void stateCode() {
					//merge the last switch and default in the current instruction block
					CaseInstruction defaultInstruction = (CaseInstruction) containerStack.peek().removeInstruction();
					SwitchInstruction switchInstruction = (SwitchInstruction) containerStack.peek().removeInstruction();
					switchInstruction.defaultInstruction = defaultInstruction;
					containerStack.peek().addInstruction(switchInstruction);
				}
				
			});
			
			states.get(MERGE_SWITCH_DEFAULT_STATE).transition = new SimpleTransition(SWITCH_TAG);
			
			
			/* ATOMIC INSTRUCTION */
			states.put(ATOMIC_INSTRUCTION_STATE, new State(){

				@Override
				public void stateCode() {
					Instruction atomicInstruction = null;
					if(currentEvent.tagName.equals(EXECUTE_TAG)){						//construct an assign expression
						ExpressionContainerInstruction eci = new ExpressionContainerInstruction();
						eci.expression = constructExpression(currentEvent.attributes.get(EXPRESSION_ATTRIBUTE));
						atomicInstruction = eci;
						
					} else if(currentEvent.tagName.equals(GET_NEXT_MESSAGE_TAG)){		//construct a get_next_message
						String timerString = currentEvent.attributes.get(TIMER_ATTRIBUTE);
						GetNextMessageInstruction gnmi = new GetNextMessageInstruction();
						if(timerString!=null){
							gnmi.timer = constructExpression(timerString);
						}
						atomicInstruction = gnmi;
						
					} else if(currentEvent.tagName.equals(SEND_MESSAGE_TAG)){			//construct a send_message
						SendMessageInstruction smi = new SendMessageInstruction();
						smi.to = constructExpression(currentEvent.attributes.get(TO_ATTRIBUTE));
						smi.data = constructExpression(currentEvent.attributes.get(DATA_ATTRIBUTE));
						atomicInstruction = smi;
					} else if(currentEvent.tagName.equals(BREAK_TAG)){					//construct a break instruction
						ValueInstruction vi = new ValueInstruction();
						vi.value = SpecialSymbols.BREAK;
						atomicInstruction = vi;
					} else if(currentEvent.tagName.equals(BREAK_TAG)){					//construct a continue instruction
						ValueInstruction vi = new ValueInstruction();
						vi.value = SpecialSymbols.CONTINUE;
						atomicInstruction = vi;
					} else if(currentEvent.tagName.equals(RETURN_TAG)){					//construct a return expression
						Object value = currentEvent.attributes.get(VALUE_ATTRIBUTE);
						if(value != null){										//returns a value
							atomicInstruction = constructExpression(value.toString());
						} else {												//returns null
							atomicInstruction = new VariableInstruction();
						}
					}
						
					containerStack.peek().addInstruction(atomicInstruction);
				}
				
			});
			
			states.get(ATOMIC_INSTRUCTION_STATE).transition = new Transition(){

				@Override
				public String transitionCode() {
					getNextEvent();
					
					if(currentEvent.eventType.equals(EventType.EndElement)){
						return CODE_TAG;
					}
					errorMessage = ATOMIC_INSTRUCTION_STATE;
					return ERROR_STATE;
				}
				
			};
			
			
			
			/**** END STATE ****/
			states.put(END_STATE, new State(){

				@Override
				public void stateCode() {
					stopSafely();
					synchronized (DeserializationAutomaton.this){
						DeserializationAutomaton.this.notify();
					}
				}
				
			});
			
			states.get(END_STATE).transition = new SimpleTransition(END_STATE);
			
			
			/**** ERROR STATE ****/
			states.put(ERROR_STATE, new State(){

				@Override
				public void stateCode() {
					stopSafely();
					synchronized (DeserializationAutomaton.this){
						DeserializationAutomaton.this.notify();
					}
				}
				
			});
			
			states.get(ERROR_STATE).transition = new SimpleTransition(ERROR_STATE);
			
		}
		
		public void waitForFinish(){
			synchronized (DeserializationAutomaton.this) {
				if (this.currentStateName == null ||
						!(this.currentStateName.equals(END_STATE) || this.currentStateName.equals(ERROR_STATE))) {
					try {
						DeserializationAutomaton.this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		/**
		 * Gets the next message from the messageList and puts it in the currentMessage member, then
		 * puts the event from the message and puts it in the currentEvent member.
		 */
		protected void getNextEvent(){
			getNextMessage();
			currentEvent = (Event) currentMessage.data;
		}
		

		/**
		 * Given a String expression it constructs an instruction tree representing the expression and returns it.
		 * @param expression - the String representation of the expression
		 * @return - the Instruction tree representing the expression
		 */
		protected Instruction constructExpression(String expression){
			Stack<Instruction> operators = new Stack<Instruction>();
			Stack<Instruction> operands = new Stack<Instruction>();
			Instruction currentToken;						//holds the current token received from the lexer
			LexerAutomaton lexer = new LexerAutomaton();	//the automaton which extracts the tokens
			boolean hasMoreTokens = true;
			
			currentToken = new OpenedBracketInstruction();
			lexer.setExpression(expression);
			
			while(true) {
				if(currentToken instanceof OpenedBracketInstruction){	//if it finds opened bracket it pushes it on operators stack
					operators.push(currentToken);
					
				} else if(currentToken instanceof ClosedBracketInstruction){	//if it finds closed bracket
					while(!(operators.peek() instanceof OpenedBracketInstruction)){
						//it pops from the operators stack until opened bracket is found
						Instruction operator = operators.pop();
						if(operator instanceof OneOperandInstruction){					//unary operator
							((OneOperandInstruction)operator).operand = operands.pop();
							operands.push(operator);
						} else if(operator instanceof TwoOperandsInstruction){			//binary operator
							((TwoOperandsInstruction)operator).rightOperand = operands.pop();
							((TwoOperandsInstruction)operator).leftOperand = operands.pop();
							operands.push(operator);
						}
					}
					operators.pop();	//at the end it pops the opened bracket from the operators stack
					
				} else if(currentToken instanceof OneOperandInstruction || currentToken instanceof TwoOperandsInstruction){
					Instruction operator = operators.pop();
					//pops until it finds an operator with lower precedence
					while(precedence.get(operator.getClass()) >= precedence.get(currentToken.getClass())){
						if(operator instanceof OneOperandInstruction){					//unary operator
							((OneOperandInstruction)operator).operand = operands.pop();
							operands.push(operator);
						} else if(operator instanceof TwoOperandsInstruction){			//binary operator
							((TwoOperandsInstruction)operator).rightOperand = operands.pop();
							((TwoOperandsInstruction)operator).leftOperand = operands.pop();
							operands.push(operator);
						}
						
						operator = operators.pop();
					}
					//at the ends it pushes the popped operator and the current token in the operators stack
					operators.push(operator);
					operators.push(currentToken);
					
				} else {				//if all the above failed, then it's an operand and it's pushed on the operands stack
					operands.push(currentToken);
				}
				
				//get the next token
				lexer.run();
				if(lexer.getResultInstruction() != null && hasMoreTokens){
					currentToken = lexer.getResultInstruction();
				} else if(hasMoreTokens){
					currentToken = new ClosedBracketInstruction();
					hasMoreTokens = false;
				} else {
					break;
				}
			}
			
			try{
				return operands.pop();		//at the end the element remaining in the operands stack is the result
			} catch (EmptyStackException e) {
				return null;
			}
		}
		
		/**
		 * Automaton that parses an expression until it finds a token and transforms it in an Instruction.
		 * @author Micu Andrei
		 *
		 */
		protected class LexerAutomaton extends Automaton{
			protected static final String IDLE_STATE = "IDLE_STATE";
			protected static final String PROCESS_STATE = "PROCESS_STATE";
			protected static final String NAME = "NAME";
			protected static final String INDEX = "INDEX";
			protected static final String PARAMETERS = "PARAMETERS";
			protected static final String VARIABLE = "VARIABLE";
			protected static final String VARIABLE_WITH_INDEX = "VARIABLE_WITH_INDEX";
			protected static final String FUNCTION = "FUNCTION";
			protected static final String NUMBER = "NUMBER";
			protected static final String RATIONAL_NUMBER = "RATIONAL_NUMBER";
			protected static final String INTEGER_VALUE = "INTEGER_VALUE";
			protected static final String DOUBLE_VALUE = "DOUBLE_VALUE";
			protected static final String STRING_VALUE = "STRING_VALUE";
			protected static final String BOOLEAN_VALUE = "BOOLEAN_VALUE";
			protected static final String CHECK_OPERATOR = "CHECK_OPERATOR";
			protected static final String OPERATOR = "OPERATOR";
			protected static final String END_STATE = "END_STATE";
			
			protected static final char NULL_CHAR = 0; //signifies that the character doesn't exist (the cursor passed the end of expression)
			
			protected String expression;				//holds the expression passed by the ExpressionBuilderAtuomaton
			protected int cursor = -1;					//holds the position of the cursor
			protected ArrayList<Object> accumulators;	//holds the significant values of the token
			protected Instruction resultInstruction;	//holds the instruction passed as a result
			protected char currentChar;					//auxiliary used to retain the character at the cursor position
			
			public int getCursor() {
				return cursor;
			}

			public Instruction getResultInstruction() {
				return resultInstruction;
			}

			public void setExpression(String expression) {
				this.expression = expression;
			}

			public LexerAutomaton() {
				super();
				currentStateName = IDLE_STATE;
				
				/* IDLE STATE */
				states.put(IDLE_STATE, new State(){

					@Override
					public void stateCode() {
						accumulators = new ArrayList<Object>();
						accumulators.add(new StringBuilder());
					}
					
				});
				
				states.get(IDLE_STATE).transition = new SimpleTransition(PROCESS_STATE);
				
				
				/* PROCESS STATE */
				states.put(PROCESS_STATE, new StepState());
				
				states.get(PROCESS_STATE).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(currentChar == NULL_CHAR){	//expression finish
							resultInstruction = null;
							return END_STATE;
						} else if(currentChar == ' '){
							return PROCESS_STATE;
						} else if(Character.isLetter(currentChar)) {
							return NAME;
						} else if(Character.isDigit(currentChar)) {
							return NUMBER;
						} else if(currentChar == '\'') {
							return STRING_VALUE;
						} else if(currentChar == '.') {
							return BOOLEAN_VALUE;
						} else if(currentChar == '-'){
							return CHECK_OPERATOR;
						} else {
							return OPERATOR;
						}
					}
					
				};
				
				
				/* NAME STATE */
				states.put(NAME, new AccumulateAndStepState());
				
				states.get(NAME).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(Character.isLetterOrDigit(currentChar) || currentChar == '_'){
							return NAME;
						} else if(currentChar == '[') {
							return INDEX;
						} else if(currentChar == '(') {
							return PARAMETERS;
						} else {
							return VARIABLE;
						}
					}
					
				};
				

				/* INDEX STATE */
				states.put(INDEX, new State(){

					@Override
					public void stateCode() {					
						//calculate where the expression starts and ends
						boolean insideString = false;
						int initialCursorPosition = cursor+1, bracketOpened = 1;
						while(true){
							nextChar();
							if(currentChar == '\''){
								insideString = !insideString;
							}
							if(!insideString){
								if(currentChar == '['){
									bracketOpened++;
								} else if(currentChar == ']'){
									if(bracketOpened == 1){
										break;
									} else {
										bracketOpened--;
									}
								}
							}
						}
						
						//create an instruction from the expression and add it to the accumulators list
						accumulators.add(constructExpression(expression.substring(initialCursorPosition, cursor)));
						nextChar();
					}
					
				});
				
				states.get(INDEX).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(currentChar == '[') {
							return INDEX;
						} else {
							return VARIABLE_WITH_INDEX;
						}
					}
					
				};

				
				/* VARIABLE STATE */
				states.put(VARIABLE, new State(){

					@Override
					public void stateCode() {
						VariableInstruction var = new VariableInstruction();
						var.name = accumulators.get(0).toString();
						resultInstruction = var;
						cursor--;
					}
					
				});
				
				states.get(VARIABLE).transition = new SimpleTransition(END_STATE);
				
				
				/* VARIABLE WITH INDEX STATE */
				states.put(VARIABLE_WITH_INDEX, new State(){

					@Override
					public void stateCode() {
						VariableWithIndexInstruction vwii = new VariableWithIndexInstruction();
						vwii.name = accumulators.get(0).toString();
						vwii.indexValues = new ArrayList<Instruction>();
						for(int i=1; i<accumulators.size(); i++){
							vwii.indexValues.add((Instruction) accumulators.get(i));
						}
						resultInstruction = vwii;
						cursor--;
					}
					
				});
				
				states.get(VARIABLE_WITH_INDEX).transition = new SimpleTransition(END_STATE);
				
				
				/* PARAMETERS STATE */
				states.put(PARAMETERS, new State(){

					@Override
					public void stateCode() {
						//calculate where the first parameter starts and ends
						boolean insideString = false;
						int initialCursorPosition = cursor+1, bracketOpened = 1;
						
						while(true){
							nextChar();
							if(currentChar == '\''){
								insideString = !insideString;
							}
							if(!insideString){
								if(currentChar == '('){
									bracketOpened++;
								} else if(currentChar == ')'){
									if(bracketOpened == 1){
										break;
									} else {
										bracketOpened--;
									}
								} else if(currentChar == ',' && bracketOpened == 1){
									break;
								}
							}
						}
						
						//create an instruction from the parameter and add it to the accumulators list
						Instruction parameter = constructExpression(expression.substring(initialCursorPosition, cursor));
						if(parameter != null){
							accumulators.add(parameter);
						}
					}
					
				});
				
				states.get(PARAMETERS).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(currentChar == ','){
							return PARAMETERS;
						} else {
							return FUNCTION;
						}
					}
					
				};
				
				
				/* FUNCTION STATE */
				states.put(FUNCTION, new State(){

					@Override
					public void stateCode() {
						//create the specific function from the name in the first accumulator
						FunctionInstruction fi;
						String functionName = accumulators.get(0).toString();
						if(functionName.equals(GET_CURRENT_MESSAGE_FROM_FUNCTION)){
							fi = new GetCurrentMessageFromInstruction();
						} else if(functionName.equals(GET_CURRENT_MESSAGE_DATA_FUNCTION)){
							fi = new GetCurrentMessageDataInstruction();
						} else if(functionName.equals(IS_CURRENT_MESSAGE_NULL_FUNCTION)){
							fi = new IsCurrentMessageNullInstruction();
						} else if(functionName.equals(EXECUTE_TRANSITION_FUNCTION)){
							ExecuteTransitionInstruction eti = new ExecuteTransitionInstruction();
							for(int i=containerStack.size()-1; i>=0; i--){
								if(containerStack.get(i) instanceof TransitionGroup){
									eti.owner = (TransitionGroup) containerStack.get(i);
									break;
								}
							}
							fi = eti;
						} else if(functionName.equals(SIZE_FUNCTION)){
							fi = new SizeInstruction();
						} else if(functionName.equals(SPLIT_FUNCTION)){
							fi = new SplitInstruction();
						} else if(functionName.equals(PARSE_INTEGER_FUNCTION)){
							fi = new ParseIntegerInstruction();
						} else {
							fi = null;
						}
						
						//add the parameters from the rest of the list
						int numberOfParameters = accumulators.size()-1;
						if(numberOfParameters > 0){
							fi.parameters = new Instruction[accumulators.size()-1];
							for(int i=1; i<accumulators.size(); i++){
								fi.parameters[i-1] = (Instruction) accumulators.get(i);
							}
						}
						resultInstruction = fi;
					}
					
				});
				
				states.get(FUNCTION).transition = new SimpleTransition(END_STATE);
				
				
				/* NUMBER STATE */
				states.put(NUMBER, new AccumulateAndStepState());
				
				states.get(NUMBER).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(Character.isDigit(currentChar)){
							return NUMBER;
						} else if(currentChar == '.'){
							return RATIONAL_NUMBER;
						} else {
							return INTEGER_VALUE;
						}
					}
					
				};
				
				
				/* RATIONAL NUMBER STATE */
				states.put(RATIONAL_NUMBER, new AccumulateAndStepState());
				
				states.get(RATIONAL_NUMBER).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(Character.isDigit(currentChar)){
							return RATIONAL_NUMBER;
						} else {
							return DOUBLE_VALUE;
						}
					}
					
				};
				
				
				/* INTEGER VALUE STATE */
				states.put(INTEGER_VALUE, new State(){

					@Override
					public void stateCode() {
						ValueInstruction val = new ValueInstruction();
						val.value = Integer.parseInt(accumulators.get(0).toString());
						resultInstruction = val;
						cursor--;
					}
					
				});
				
				states.get(INTEGER_VALUE).transition = new SimpleTransition(END_STATE);

				
				/* DOUBLE VALUE STATE */
				states.put(DOUBLE_VALUE, new State(){

					@Override
					public void stateCode() {
						ValueInstruction val = new ValueInstruction();
						val.value = Double.parseDouble(accumulators.get(0).toString());
						resultInstruction = val;
						cursor--;
					}
					
				});
				
				states.get(DOUBLE_VALUE).transition = new SimpleTransition(END_STATE);
				
				
				/* STRING VALUE STATE */
				states.put(STRING_VALUE, new State(){

					@Override
					public void stateCode() {
						ValueInstruction val = new ValueInstruction();

						//calculate where the string starts and ends
						int initialCursorPosition = cursor+1;
						do{
							nextChar();
						} while (currentChar != '\'');
						
						val.value = new String(expression.substring(initialCursorPosition, cursor));
						resultInstruction = val;
					}
					
				});
				
				states.get(STRING_VALUE).transition = new SimpleTransition(END_STATE);

				
				/* BOOLEAN VALUE STATE */
				states.put(BOOLEAN_VALUE, new State(){

					@Override
					public void stateCode() {
						ValueInstruction val = new ValueInstruction();

						//calculate where the boolean expression starts and ends
						int initialCursorPosition = cursor+1;
						do{
							nextChar();
						} while (currentChar != '.');
						
						val.value = (expression.substring(initialCursorPosition, cursor).equals("T"));
						resultInstruction = val;
					}
					
				});
				
				states.get(BOOLEAN_VALUE).transition = new SimpleTransition(END_STATE);
				
				
				/* CHECK OPERATOR STATE */
				states.put(CHECK_OPERATOR, new NullState());
				
				states.get(CHECK_OPERATOR).transition = new Transition(){

					@Override
					public String transitionCode() {
						if(resultInstruction == null 			//check if there was no previous instruction
								|| resultInstruction instanceof OneOperandInstruction 	//or if it was an operator
								|| resultInstruction instanceof TwoOperandsInstruction){
							return NUMBER;
						} else {
							return OPERATOR;
						}
					}
					
				};
				
				
				/* OPERATOR STATE */
				states.put(OPERATOR, new State(){

					@Override
					public void stateCode() {
						if(currentChar == '+'){
							resultInstruction = new AdditionInstruction();
						} else if(currentChar == '-'){
							resultInstruction = new SubtractionInstruction();
						} else if(currentChar == '*'){
							resultInstruction = new MultiplicationInstruction();
						} else if(currentChar == '/'){
							resultInstruction = new DivisionInstruction();
						} else if(currentChar == '%'){
							resultInstruction = new ModuloInstruction();
						} else if(currentChar == '='){
							if(expression.charAt(cursor+1) != '='){
								resultInstruction = new AssignInstruction();
							} else {
								cursor++;
								resultInstruction = new EqualsInstruction();
							}
						} else if(currentChar == '!'){
							if(expression.charAt(cursor+1) != '='){
								resultInstruction = new NotInstruction();
							} else {
								cursor++;
								resultInstruction = new NotEqualsInstruction();
							}
						} else if(currentChar == '<'){
							if(expression.charAt(cursor+1) != '='){
								resultInstruction = new LessInstruction();
							} else {
								cursor++;
								resultInstruction = new LessOrEqualInstruction();
							}
						} else if(currentChar == '>'){
							if(expression.charAt(cursor+1) != '='){
								resultInstruction = new GreaterInstruction();
							} else {
								cursor++;
								resultInstruction = new GreaterOrEqualInstruction();
							}
						} else if((currentChar == '&') && (expression.charAt(cursor+1) == '&')) {
							cursor++;
							resultInstruction = new AndInstruction();
						} else if((currentChar == '|') && (expression.charAt(cursor+1) == '|')) {
							cursor++;
							resultInstruction = new OrInstruction();
						} else if(currentChar == '('){
							resultInstruction = new OpenedBracketInstruction();
						} else if(currentChar == ')'){
							resultInstruction = new ClosedBracketInstruction();
						}
					}
					
				});
				
				states.get(OPERATOR).transition = new SimpleTransition(END_STATE);
				
				
				/* END STATE */
				states.put(END_STATE, new State(){

					@Override
					public void stateCode() {
						LexerAutomaton.this.stopSafely();
					}
					
				});
				
				states.get(END_STATE).transition = new SimpleTransition(IDLE_STATE);
			}

			/**
			 * Moves the cursor forward with one position and updates the currentChar. If the cursor passes the
			 * end of the expression, then the currentChar takes NULL_CHAR value.
			 */
			protected void nextChar() {
				cursor++;
				if(cursor < expression.length()){
					currentChar = expression.charAt(cursor);
				} else {
					currentChar = NULL_CHAR;
				}
			}
			
			/**
			 * Appends the current character to the first accumulator which is the name accumulator.
			 */
			protected void addToFirstAccumulator(){
				((StringBuilder)accumulators.get(0)).append(currentChar);
			}
			
			/**
			 * A state which calls nextChar().
			 * @author Micu Andrei
			 *
			 */
			protected class StepState extends State{

				@Override
				public void stateCode() {
					nextChar();
				}
				
			}
			
			/**
			 * A state which calls addToFirstAccumulator(), then nextChar().
			 * @author Micu Andrei
			 *
			 */
			protected class AccumulateAndStepState extends State{

				@Override
				public void stateCode() {
					addToFirstAccumulator();
					nextChar();
				}
				
			}
		}
	}
	
	/**
	 * Class which represents the opened bracket. It is used only for parsing XML Automatons, it is not included in the
	 * instruction tree.
	 * @author Micu Andrei
	 *
	 */
	protected class OpenedBracketInstruction implements Instruction{

		public Object code() {
			return null;
		}
		
	}
	
	/**
	 * Class which represents the closed bracket. It is used only for parsing XML Automatons, it is not included in the
	 * instruction tree.
	 * @author Micu Andrei
	 *
	 */
	protected class ClosedBracketInstruction implements Instruction{

		public Object code() {
			return null;
		}
		
	}
	
	/**
	 * HashMap containing the precedence of the operators. The keys are the class objects of each operator class.
	 */
	protected static HashMap<Class<?>, Integer> precedence = new HashMap<Class<?>, Integer>();
	static{
		precedence.put(MultiplicationInstruction.class, 4);
		precedence.put(DivisionInstruction.class, 4);
		precedence.put(ModuloInstruction.class, 4);
		precedence.put(AdditionInstruction.class, 3);
		precedence.put(SubtractionInstruction.class, 3);
		precedence.put(LessInstruction.class, 2);
		precedence.put(LessOrEqualInstruction.class, 2);
		precedence.put(GreaterInstruction.class, 2);
		precedence.put(GreaterOrEqualInstruction.class, 2);
		precedence.put(EqualsInstruction.class, 2);
		precedence.put(NotEqualsInstruction.class, 2);
		precedence.put(AndInstruction.class, 1);
		precedence.put(OrInstruction.class, 1);
		precedence.put(NotInstruction.class, 1);
		precedence.put(AssignInstruction.class, 0);
		precedence.put(OpenedBracketInstruction.class, -1);
		precedence.put(ClosedBracketInstruction.class, -1);
	}
}
